using System;
using System.Runtime.InteropServices;

namespace WMVConvert
{
	public enum StreamKind
	{
		General,
		Visual,
		Audio,
		Text,
		Chapters,
		Image,
		Menu,
		Max
	}

	public enum InfoKind
	{
		Name,
		Text,
		Measure,
		Options,
		NameText,
		MeasureText,
		Info,
		HowTo,
		Max
	}

	public enum InfoOptions
	{
		ShowInInform,
		Reserved,
		ShowInSupported,
		TypeOfValue,
		Max
	}

	public class MediaInfo : IDisposable, WMVConvert.IMediaInfo
	{
		private IntPtr handle;
		private string fileName;

		public MediaInfo(string fileName)
		{
			this.fileName = fileName;
			this.handle = MediaInfo_New();
			Open(fileName);
		}

		public string SourceFile
		{
			get { return fileName; }
		}

		public string TargetFile
		{
			get { return string.Empty; }
		}

		public int Segment
		{
			get { return -1; }
		}

		public int State
		{
			get { return MediaInfo_State_Get(this.handle); }
		}

		public int VideoCount
		{
			get { return GetIntProperty(StreamKind.General, 0, "VideoCount", InfoKind.Text, InfoKind.Name); }
		}

		public int AudioCount
		{
			get { return GetIntProperty(StreamKind.General, 0, "AudioCount", InfoKind.Text, InfoKind.Name); }
		}

		public int TextCount
		{
			get { return GetIntProperty(StreamKind.General, 0, "TextCount", InfoKind.Text, InfoKind.Name); }
		}

		public string Format
		{
			get { return GetProperty(StreamKind.General, 0, "Format", InfoKind.Text, InfoKind.Name); }
		}

		public long PlayTime
		{
			get
			{
				return GetLongProperty(StreamKind.General, 0, "PlayTime", InfoKind.Text, InfoKind.Name);
			}
		}

		public string VideoCodec
		{
			get { return GetProperty(StreamKind.Visual, 0, "Codec", InfoKind.Text, InfoKind.Name); }
		}

		public int VideoPeakBitRate
		{
			get { return GetIntProperty(StreamKind.Visual, 0, "PeakBitRate", InfoKind.Text, InfoKind.Name); }
		}

		public int VideoBitRate
		{
			get { return GetIntProperty(StreamKind.Visual, 0, "BitRate", InfoKind.Text, InfoKind.Name); }
		}

		public int Height
		{
			get { return GetIntProperty(StreamKind.Visual, 0, "Height", InfoKind.Text, InfoKind.Name); }
		}

		public int Width
		{
			get { return GetIntProperty(StreamKind.Visual, 0, "Width", InfoKind.Text, InfoKind.Name); }
		}

		public double FrameRate
		{
			get { return GetDoubleProperty(StreamKind.Visual, 0, "FrameRate", InfoKind.Text, InfoKind.Name); }
		}

		public double AspectRatio
		{
			get { return GetDoubleProperty(StreamKind.Visual, 0, "AspectRatio", InfoKind.Text, InfoKind.Name); }
		}

		public string AudioCodec
		{
			get { return GetProperty(StreamKind.Audio, 0, "Codec", InfoKind.Text, InfoKind.Name); }
		}

		public int AudioBitRate
		{
			get { return GetIntProperty(StreamKind.Audio, 0, "BitRate", InfoKind.Text, InfoKind.Name); }
		}

		public int Channels
		{
			get { return GetIntProperty(StreamKind.Audio, 0, "Channel(s)", InfoKind.Text, InfoKind.Name); }
		}

		public long SamplingRate
		{
			get { return GetLongProperty(StreamKind.Audio, 0, "SamplingRate", InfoKind.Text, InfoKind.Name); }
		}

		public int Resolution
		{
			get { return GetIntProperty(StreamKind.Audio, 0, "Resolution", InfoKind.Text, InfoKind.Name); }
		}

		public int Start
		{
			get { return 0; }
		}

		public int End
		{
			get { return 0; }
		}

		public string Title
		{
			get { return GetProperty(StreamKind.General, 0, "Title", InfoKind.Text, InfoKind.Name); }
		}

		public string Description
		{
			get { return GetProperty(StreamKind.General, 0, "Description", InfoKind.Text, InfoKind.Name); }
		}

		public string Author
		{
			get { return GetProperty(StreamKind.General, 0, "Author", InfoKind.Text, InfoKind.Name); }
		}

		public string Copyright
		{
			get { return GetProperty(StreamKind.General, 0, "Copyright", InfoKind.Text, InfoKind.Name); }
		}

		#region MediaInfo private implementation

		private void Close()
		{
			MediaInfo_Close(this.handle);
		}

		private int GetCount(StreamKind StreamKind, [Optional, DefaultParameterValue(-1)] int StreamNumber)
		{
			return MediaInfo_Count_Get(this.handle, StreamKind, StreamNumber);
		}

		private int GetIntProperty(StreamKind streamKind, int streamNumber, string parameter, [Optional, DefaultParameterValue(InfoKind.Name)] InfoKind kindOfInfo, [Optional, DefaultParameterValue(InfoKind.Name)] InfoKind kindOfSearch)
		{
			double value = GetDoubleProperty(streamKind, streamNumber, parameter, kindOfInfo,kindOfSearch);
			return (int)Math.Round(value,0);
		}

		private double GetDoubleProperty(StreamKind streamKind, int streamNumber, string parameter, [Optional, DefaultParameterValue(InfoKind.Name)] InfoKind kindOfInfo, [Optional, DefaultParameterValue(InfoKind.Name)] InfoKind kindOfSearch)
		{
			string value = GetProperty(streamKind, streamNumber, parameter, kindOfInfo,kindOfSearch);
			double castedValue = 0;
			double.TryParse(value, out castedValue);
			return castedValue;
		}

		private long GetLongProperty(StreamKind streamKind, int streamNumber, string parameter, [Optional, DefaultParameterValue(InfoKind.Name)] InfoKind kindOfInfo, [Optional, DefaultParameterValue(InfoKind.Name)] InfoKind kindOfSearch)
		{
			string value = GetProperty(streamKind, streamNumber, parameter, kindOfInfo, kindOfSearch);
			long castedValue = 0;
			long.TryParse(value, out castedValue);
			return castedValue;
		}

		private string GetProperty(StreamKind StreamKind, int StreamNumber, int Parameter, [Optional, DefaultParameterValue(InfoKind.Name)] InfoKind KindOfInfo)
		{
			return Marshal.PtrToStringUni(MediaInfo_GetI(this.handle, StreamKind, StreamNumber, Parameter, KindOfInfo));
		}

		private string GetProperty(StreamKind StreamKind, int StreamNumber, string Parameter, [Optional, DefaultParameterValue(InfoKind.Name)] InfoKind KindOfInfo, [Optional, DefaultParameterValue(InfoKind.Name)] InfoKind KindOfSearch)
		{
			return Marshal.PtrToStringUni(MediaInfo_Get(this.handle, StreamKind, StreamNumber, ref Parameter, KindOfInfo, KindOfSearch));
		}

		private string Inform()
		{
			return Marshal.PtrToStringUni(MediaInfo_Inform(this.handle, 0));
		}

		[DllImport("MediaInfo.DLL", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
		private static extern void MediaInfo_Close(IntPtr handle);
		[DllImport("MediaInfo.DLL", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
		private static extern int MediaInfo_Count_Get(IntPtr handle, StreamKind StreamKind, int StreamNumber);
		[DllImport("MediaInfo.DLL", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
		private static extern void MediaInfo_Delete(IntPtr handle);
		[DllImport("MediaInfo.DLL", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
		private static extern IntPtr MediaInfo_Get(IntPtr handle, StreamKind StreamKind, int StreamNumber, [MarshalAs(UnmanagedType.VBByRefStr)] ref string Parameter, InfoKind KindOfInfo, InfoKind KindOfSearch);
		[DllImport("MediaInfo.DLL", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
		private static extern IntPtr MediaInfo_GetI(IntPtr handle, StreamKind StreamKind, int StreamNumber, int Parameter, InfoKind KindOfInfo);
		[DllImport("MediaInfo.DLL", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
		private static extern IntPtr MediaInfo_Inform(IntPtr handle, int Reserved);
		[DllImport("MediaInfo.DLL", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
		private static extern IntPtr MediaInfo_New();
		[DllImport("MediaInfo.DLL", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
		private static extern int MediaInfo_Open(IntPtr handle, [MarshalAs(UnmanagedType.VBByRefStr)] ref string FileName);
		[DllImport("MediaInfo.DLL", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
		private static extern IntPtr MediaInfo_Option(IntPtr handle, [MarshalAs(UnmanagedType.VBByRefStr)] ref string Option_, [MarshalAs(UnmanagedType.VBByRefStr)] ref string Value);
		[DllImport("MediaInfo.DLL", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
		private static extern int MediaInfo_State_Get(IntPtr handle);

		private int Open(string FileName)
		{
			return MediaInfo_Open(this.handle, ref FileName);
		}

		private string GetOption(string Option__, [Optional, DefaultParameterValue("")] string Value)
		{
			return Marshal.PtrToStringUni(MediaInfo_Option(this.handle, ref Option__, ref Value));
		}

#endregion

		#region IDisposable

		bool disposed;

		// Implement IDisposable.
		// Do not make this method virtual.
		// A derived class should not be able to override this method.
		public void Dispose()
		{
			Dispose(true);
			// Take yourself off the Finalization queue 
			// to prevent finalization code for this object
			// from executing a second time.
			GC.SuppressFinalize(this);
		}

		// Dispose(bool disposing) executes in two distinct scenarios.
		// If disposing equals true, the method has been called directly
		// or indirectly by a user's code. Managed and unmanaged resources
		// can be disposed.
		// If disposing equals false, the method has been called by the 
		// runtime from inside the finalizer and you should not reference 
		// other objects. Only unmanaged resources can be disposed.
		protected virtual void Dispose(bool disposing)
		{
			// Check to see if Dispose has already been called.
			if (!this.disposed)
			{
				// If disposing equals true, dispose all managed 
				// and unmanaged resources.
				if (disposing)
				{
					// Dispose managed resources.
				}
				// Release unmanaged resources. If disposing is false, 
				// only the following code is executed.
				if (handle != IntPtr.Zero)
				{
					MediaInfo_Delete(this.handle);
					handle = IntPtr.Zero;
				}
				// Note that this is not thread safe.
				// Another thread could start disposing the object
				// after the managed resources are disposed,
				// but before the disposed flag is set to true.
				// If thread safety is necessary, it must be
				// implemented by the client.
			}
			disposed = true;
		}

		// Use C# destructor syntax for finalization code.
		// This destructor will run only if the Dispose method 
		// does not get called.
		// It gives your base class the opportunity to finalize.
		// Do not provide destructors in types derived from this class.
		~MediaInfo()
		{
			// Do not re-create Dispose clean-up code here.
			// Calling Dispose(false) is optimal in terms of
			// readability and maintainability.
			Dispose(false);
		}

		#endregion

	}
}

